/**
 * @fileOverview 模块加载规范 一些基础函数
 * @author goodzsq@gmail.com
 */

//运行环境检测

/**
 * @namespace gg命名空间
 */
var gg = gg || {
    ver: '0.1',
    project: '',
    runAt: (function() {
        var result = {};
        if (typeof window === 'object' && window === window.window) {
            return {browser: true, native: false, server: false};
        } else if (typeof global === 'object' && global === global.global) {
            if (typeof cc.Node === 'function') {
                return {browser: false, native: true, server: false};
            } else {
                return {browser: false, native: false, server: true};
            }
        }
        return result;
    })()
};

/**
 * 设置/获取工程路径
 * @param {type} path
 * @return {String}
 */
gg.path = function(path) {
    if (arguments.length === 0) {
        if (!gg.path._path)
            return '';
        else
            return gg.path._path;
    } else {
        gg.path._path = path;
    }
};

/**
 * 加载并缓存模块,保证只加载一次,模块名称以a.b.c的形式代替路径形式
 * @param {String} moduleName 模块名称
 * @returns {Function | Object} 
 * @example
 * //load file:ui/jq/Button.js
 * var Button = gg.module('ui.jq.Button')
 */
gg.module = function(moduleName) {
    gg.error(moduleName + 'not implement');
};

/**
 * 错误处理
 * @param {String} msg 错误消息
 * @returns {undefined}
 */
gg.error = function(msg) {
    throw Error(msg);
};

/**
 * 空函数
 */
gg.noop = function() {
};

/* 容纳所有创建的实例 */
gg.objectContainer = {};

/**
 * 自动生成一个确保唯一的对象id
 * @param {String} prefix 前缀
 * @return {String}
 */
gg.uuid = function(prefix) {
    prefix = (prefix == null ? '' : prefix) + '_';
    if (!gg.uuid._data[prefix]) {
        gg.uuid._data[prefix] = 0;
    }
    gg.uuid._data[prefix] += 1;
    while (gg.objectContainer[prefix + gg.uuid._data[prefix]]) {
        gg.uuid._data[prefix] += 1;
    }
    return prefix + gg.uuid._data[prefix];
};
gg.uuid._data = {};

/**
 * 创建或者获取对象
 * @param {String | Object} obj 参数为string时候 根据id值查询对象 为Object时创建对象
 * @param {BaseObject} parent 当创建对象时,指定对象的父亲
 * @returns {BaseObject}
 */
gg.object = function(obj, parent) {
    if (gg.type(obj) === 'string') {
        return gg.objectContainer[obj];
    }
    if (obj instanceof gg.module('BaseObject')) {
        if (parent) {
            obj.parent = parent;
        }
        return obj;
    }
    if (gg.type(obj) !== 'object') {
        return undefined;
    }
    var classType = obj.classType || 'BaseObject';
    var classFn = gg.module(classType);
    if (classFn && classFn.create) {
        return classFn.create(obj, parent);
    } else {
        gg.error('classType not Exist:' + classType);
    }
};

gg.equal = function(a, b) {
    var typea = Object.prototype.toString.call(a);
    var typeb = Object.prototype.toString.call(b);
    if (typea !== typeb) {
        return false;
    }
    if (typea === '[object Object]') {
        if (a.constructor.classType && b.constructor.classType) {
            return gg.equal(a.toJSON(), b.toJSON());
        } else {
            for (var i in a) {
                if (!gg.equal(a.i, b.i)) {
                    return false;
                }
            }
            for (var i in b) {
                if (!gg.equal(a[i], b[i])) {
                    return false;
                }
            }
        }
    } else if (typea === '[object Array]') {
        if (a.length !== b.length) {
            return false;
        }
        for (var i = 0; i < a.length; i++) {
            if (!gg.equal(a[i], b[i])) {
                return false;
            }
        }
    } else {
        if (a !== b) {
            return false;
        }
    }
    return true;
};

/**
 * 动态创建模块
 * @param {String} moduleName 模块名称
 * @param {Object | Function} obj 模块内容
 */
gg.createModule = function(moduleName, obj) {
    if (gg._moduleContainer[moduleName]) {
        gg.error('module already exist:' + moduleName);
    }
    if (typeof obj === 'function') {
        obj.classType = moduleName;
    }
    gg._moduleContainer[moduleName] = obj;
};

/**
 * 用模块组合的方法动态创建一个类函数
 * @param {Object} obj 属性方法事件描述
 * @param {String | Function} baseClass 基类名称
 * @param {Array} traits 组合模块名称列表
 * @return {Function} 经过组合后的类函数
 */
gg.createClass = function(obj, baseClass, traits) {
    var BaseClass = baseClass;
    if (typeof BaseClass === 'string') {
        BaseClass = gg.module(baseClass);
    }
    if (typeof BaseClass !== 'function') {
        gg.error('gg.createClass Fail, baseClass not exist:' + baseClass);
    }
    var methods = {};
    var attributes = {};
    var events = {};
    //将依赖的模块也加入特征列表
    var relay = [];
    _.each(traits, function(v, k) {
        var module = gg.module(v);
        if (module && module.requires) {
            relay = relay.concat(module.requires);
        }
        relay.push(v);
    });
    traits = _.uniq(relay);

    var plugins = {};
    for (var i = 0; i < traits.length; i++) {
        plugins[traits[i]] = true;
        var module = gg.module(traits[i]);
        if (module != null) {
            _.extend(methods, module.methods);
            _.extend(attributes, module.attributes);
            _.extend(events, module.events);
        }
    }
    _.extend(methods, obj.methods);
    _.extend(attributes, obj.attributes);
    _.extend(events, obj.events);

    var ClassFun = BaseClass.extend(methods);
    ClassFun.metadata = _.extend({}, BaseClass.metadata, attributes);
    _.each(events, function(v, k) {
        ClassFun.metadata[k] = {label: v, type: 'datatype.res.Event'};
    });
    ClassFun.traits = _.extend({}, BaseClass.traits, plugins);
    return ClassFun;
};

/**
 * 比较两个对象是否具有继承关系
 * @param {String} child
 * @param {String} parent
 * @returns {Boolean}
 */
gg.isInherit = function(child, parent) {
    var C, P, typeChild = typeof child, typeParent = typeof parent;
    if (typeChild === 'string') {
        C = gg.module(child);
    } else if (typeChild === 'function') {
        C = child;
    } else {
        C = child.constructor;
    }
    if (typeParent === 'string') {
        P = parent;
    } else if (typeParent === 'function') {
        P = parent.classType;
    } else {
        P = parent.constructor.classType;
    }
    if (typeof C.inheritInfo === 'function' && P) {
        if (C.inheritInfo().indexOf(P) >= 0) {
            return true;
        }
    }
    return false;
};

(function() {

    var class2type = {
        "[object Boolean]": "boolean",
        "[object Number]": "number",
        "[object String]": "string",
        "[object Function]": "function",
        "[object Array]": "array",
        "[object Date]": "date",
        "[object RegExp]": "regexp",
        "[object Object]": "object",
        "[object Error]": "error"
    };

    /**
     * 获取数据类型 code from jQuery
     * @param {type} obj
     * @returns {String}
     */
    gg.type = function(obj) {
        if (obj == null) {
            return String(obj);
        } else if (obj.constructor.classType) {
            return obj.constructor.classType;
        } else {
            return typeof obj === 'object' || typeof obj === 'function' ?
                    class2type[Object.prototype.toString.call(obj)] || 'object' : typeof obj;
        }
    };

    var createXHR = function() {
        if (window.XMLHttpRequest) {
            return new window.XMLHttpRequest();
        }
        else {
            return new window.ActiveXObject("Microsoft.XMLHTTP");
        }
    };

    /**
     * ajax请求
     * @param {String} url
     * @param {Object} options
     * @returns {gg.ajax.xhr}
     */
    gg.ajax = function(url, options) {
        options = options || {};
        var async = (options.async == null) ? true : false,
                method = options.method || 'GET',
                data = options.data || null,
                success = options.success || gg.noop,
                failure = options.failure || gg.noop;
        method = method.toUpperCase();
        if (method === 'GET' && data) {
            url += (url.indexOf('?') === -1 ? '?' : '&') + data;
            data = null;
        }
        var xhr = createXHR();
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                    success(xhr.responseText);
                }
                else {
                    failure(xhr);
                }
            }
        };
        xhr.open(method, url, async);
        if (method === 'POST') {
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded;');
        }
        xhr.send(data);
        return xhr;
    };

    if (gg.runAt.browser) {
        //browser
        window.gg = gg;
        window.module = {};
        window.module.exports = {};
        gg._moduleContainer = {};
        gg._moduleContainerOrder = [];//记录模块加载顺序
        gg.module = function(moduleName) {
            if (!gg._moduleContainer[moduleName]) {
                var tmp = module.exports;
                var url = '../../server/test/module.php?project='
                        + gg.path() + '&module=' + moduleName + '&t=' + Date.now();
                console.log('load module:' + moduleName, url);
                module.exports = undefined;
                gg.ajax(url, {
                    async: false,
                    success: function(result) {
                        result = JSON.parse(result);
                        if (result.code === 200) {
                            eval(result.data);
                            if (typeof module.exports === 'function') {
                                module.exports.classType = moduleName;
                            }
                            gg._moduleContainer[moduleName] = module.exports;
                            gg._moduleContainerOrder.push(moduleName);
                        } else {
                            gg.error('load module fail:' + moduleName + result.code);
                        }
                    }
                });
                module.exports = tmp;
            }
            return gg._moduleContainer[moduleName];
        };
    }
    else if (gg.runAt.server) {
        //CommonJS
        global.gg = gg;
        gg._moduleContainer = {};
        var loadModule = function(moduleName) {
            if (!gg._moduleContainer[moduleName]) {
                var parts = moduleName.split('.');
                parts.unshift(__dirname);
                var file = parts.join('/') + '.js';
                console.log('load module:' + file);
                gg._moduleContainer[moduleName] = require(file);
            }
            return gg._moduleContainer[moduleName];
        };
        gg.module = loadModule;
    }
    else {
        gg.error('todo');
    }
})();


/** @fileend */

//unittest
var UnitTest = gg.module('UnitTest');
if (gg.runAt.browser) {
    window.UnitTest = UnitTest;
} else {
    global.UnitTest = UnitTest;
}
UnitTest.add('gg.module()模块加载测试',
        function() {
            var Fn = gg.module('BaseObject');
            this.test(typeof Fn === 'function', '模块加载成功');
            this.test(Fn.classType === 'BaseObject', '向类中注入classType成功');
            var Fn1 = gg.module('BaseObject');
            this.test(Fn1 === Fn, '两次调用应该返回同一个对象');
            var Fn2 = gg.module('notexistmodule');
            this.test(Fn2 == null, '加载一个不存在的模块');
            console.log(Fn2);
        }
);

UnitTest.add('gg.object()对象工厂测试',
        function() {
            var obj = gg.object({id: 'test', a: 1, b: 2});
            this.test(obj != null, '对象应该创建成功');
            this.test(obj.id === 'test', 'id应该存在');
            this.test(obj.cid === 'test', 'cid应该等于id');

            var obj2 = gg.object('test');
            this.test(obj2 === obj, '根据id获取对象');

            obj2.destroy();
            var obj3 = gg.object('test');
            this.test(obj3 == null, '对象应该被销毁，不能根据id获取到');

            var obj4 = gg.object({a: 1, b: 2});
            this.test(obj4 != null, '无id的对象应该创建成功');
            this.test(obj4.id == null, '无id的对象');
            var cid = obj4.cid;
            this.test(cid != '', 'cid应该存在');

            var obj5 = gg.object(cid);
            this.test(obj4 === obj5, '通过cid获取对象');

            obj5.destroy();
        }
);

UnitTest.add('gg.createCass()动态创建模块(类)测试',
        function() {
            var Fn = gg.createClass({
                attributes: {
                    attr1: {
                        label: '属性1',
                        type: 'datatype.String',
                        defaultValue: 'abc'
                    }
                },
                methods: {
                    add: function() {
                        return this.get('a') + this.get('b')
                    }
                },
                events: {
                    event1: '事件1'
                }
            }, 'BaseObject', ['Move', 'Upgrade']);
            this.test(typeof Fn === 'function', '类创建成功');
            this.test(Fn.__super__.constructor.classType === 'BaseObject', '新类继承自BaseObject');
            this.test(Fn.hasTrait('Move'), '新类拥有Move特征');
            this.test(Fn.hasTrait('Upgrade'), '新类拥有Fight特征');
            this.test(!Fn.hasTrait('Avatar'), '新类不拥有Avatar特征');
            this.test(Fn.hasTrait('Shape'), '依赖关系加载：Move依赖Position');
            this.test(typeof Fn.prototype.addExp === 'function', '新类具有Upgrade里的方法');
            this.test('shape' in Fn.metadata, 'Move依赖Shape，所以具有shape属性');
            var obj = Fn.create({a: 1, b: 2});
            this.test(obj.add() === 3, '测试方法');
            this.test(obj.get('attr1') === 'abc', '测试属性默认值');
            var Resource = gg.module('Resource');
            var handler = 'function(arg1,arg2){this.set("attr1", "hello"+arg1+arg2);}';
            Resource.set('Event/mytest', 'test', handler);
            this.test(Resource.get('Event/mytest/test') === handler, '资源存取是否正常');
            obj.set('event1', 'Event/mytest/test');
            obj.triggerEvent('event1', 'world');
            this.test(obj.get('attr1') === 'helloevent1world', '事件是否被触发');
            obj.destroy();
        }
);

UnitTest.add('gg.type()测试',
        function() {
            this.test(gg.type('') === 'string', 'string');
            this.test(gg.type(null) === 'null', 'null');
            this.test(gg.type(void 0) === 'undefined', 'undefined');
            this.test(gg.type(undefined) === 'undefined', 'undefined');
            this.test(gg.type(true) === 'boolean', 'boolean');
            this.test(gg.type(false) === 'boolean', 'boolean');
            this.test(gg.type([]) === 'array', 'array');
            this.test(gg.type({}) === 'object', 'object');
            this.test(gg.type(gg.noop) === 'function', 'function');
            var obj = gg.object({a: 1, b: 2});
            this.test(gg.type(obj) === 'BaseObject', 'module class');
            this.test(gg.type(123) === 'number', 'number');
            this.test(gg.type(1.23) === 'number', 'number');
            this.test(gg.type(1 / 0) === 'number', 'number');
            this.test(gg.type(parseInt('abc')) === 'number', 'number');
            this.test(gg.type(new Date()) === 'date', 'data');
            this.test(gg.type(/abc/) === 'regexp', 'regexp');
            this.test(gg.type(Error('err')) === 'error', 'error');
            this.test(gg.isInherit('BaseEntity', 'BaseObject'), '具有继承关系');
        }
);